package simplecopier

import (
	"fmt"
	"reflect"
	"strings"
)

const (
	TAG_JSON = "json"
)

const (
	OPTIGNORE    = "-"
	OPTOMITEMPTY = "omitempty"
	OPTDIVE      = "dive"
	OPTWILDCARD  = "wildcard"
)

const (
	FLAGIGNORE = 1 << iota
	FLAGOMIEMPTY
	FLAGDIVE
	FLAGWILDCARD
)

//
// slice or array to map by json tag
//

func SliceStructToSliceMapByJsonTag(s interface{}) ([]map[string]interface{}, error) {
	return SliceStructToSliceMap(s, TAG_JSON)
}

// SliceStructToSliceMap
// slice or array to map by tag 'json, map, form'
//
func SliceStructToSliceMap(s interface{}, tag string) ([]map[string]interface{}, error) {
	v := reflect.ValueOf(s)

	// ptr to element
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Slice {
		return nil, fmt.Errorf("%s is a nil pointer", v.Kind().String())
	}

	length := v.Len()
	list := make([]interface{}, 0)
	for i := 0; i < length; i++ {
		list = append(list, v.Index(i).Interface())
	}

	res := make([]map[string]interface{}, 0)
	for _, em := range list {
		obj, err := StructToMap(em, tag)
		if err != nil {
			return nil, err
		}
		res = append(res, obj)
	}

	return res, nil
}

// StructToMapByJsonTag
// struct to map by json tag
//
func StructToMapByJsonTag(s interface{}) (map[string]interface{}, error) {
	return StructToMap(s, TAG_JSON)
}

// StructToMap
// struct to map by tag 'json, form, map'.
//
func StructToMap(s interface{}, tag string) (map[string]interface{}, error) {
	if s == nil {
		return nil, nil
	}

	v := reflect.ValueOf(s)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return nil, fmt.Errorf("%s is a nil pointer", v.Kind().String())
	}
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	// only accept struct param
	if v.Kind() != reflect.Struct {
		return nil, fmt.Errorf("s is not a struct but %s", v.Kind().String())
	}

	t := v.Type()
	res := make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		fieldType := t.Field(i)

		// ignore unexported field
		if fieldType.PkgPath != "" {
			continue
		}
		// read tag
		tagVal, flag := readTag(fieldType, tag)

		if flag&FLAGIGNORE != 0 {
			continue
		}

		fieldValue := v.Field(i)
		//if flag&flagOmiEmpty != 0 && fieldValue.IsZero() {
		//	continue
		//}

		// ignore nil pointer in field
		if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
			continue
		}
		if fieldValue.Kind() == reflect.Ptr {
			fieldValue = fieldValue.Elem()
		}

		// get kind
		switch fieldValue.Kind() {
		case reflect.Slice, reflect.Array:
			length := fieldValue.Len()
			fieldValueList := make([]interface{}, 0)
			if length != 0 {
				if fieldValue.Index(0).Kind() == reflect.String {
					for i := 0; i < length; i++ {
						fieldValueList = append(fieldValueList, fieldValue.Index(i).String())
					}
				} else {
					for i := 0; i < length; i++ {
						// TODO：兼容数组长度非0，但是值为空字符串的issue
						if fieldValue.Index(i).String() == "" {
							continue
						}
						deepRes, deepErr := StructToMap(fieldValue.Index(i).Interface(), tag)
						if deepErr != nil {
							return nil, deepErr
						}
						fieldValueList = append(fieldValueList, deepRes)
					}
				}
			}

			res[tagVal] = fieldValueList
		case reflect.Struct:
			// recursive
			deepRes, deepErr := StructToMap(fieldValue.Interface(), tag)
			if deepErr != nil {
				return nil, deepErr
			}
			if flag&FLAGDIVE != 0 {
				for k, v := range deepRes {
					res[k] = v
				}
			} else {
				res[tagVal] = deepRes
			}
		case reflect.Map:
			res[tagVal] = fieldValue
		case reflect.Chan:
			res[tagVal] = fieldValue
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
			res[tagVal] = fieldValue.Int()
		case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
			res[tagVal] = fieldValue.Uint()
		case reflect.Float32, reflect.Float64:
			res[tagVal] = fieldValue.Float()
		case reflect.String:
			if flag&FLAGWILDCARD != 0 {
				res[tagVal] = "%" + fieldValue.String() + "%"
			} else {
				res[tagVal] = fieldValue.String()
			}
		case reflect.Bool:
			res[tagVal] = fieldValue.Bool()
		case reflect.Complex64, reflect.Complex128:
			res[tagVal] = fieldValue.Complex()
		case reflect.Interface:
			res[tagVal] = fieldValue.Interface()
		default:
		}
	}

	return res, nil
}

// readTag read tag with format `json:"name,omitempty"` or `json:"-"`
// For now, only supports above format
func readTag(f reflect.StructField, tag string) (string, int) {
	val, ok := f.Tag.Lookup(tag)
	fieldTag := ""
	flag := 0

	// no tag, skip this field
	if !ok {
		flag |= FLAGIGNORE
		return "", flag
	}
	opts := strings.Split(val, ",")

	fieldTag = opts[0]
	for i := 0; i < len(opts); i++ {
		switch opts[i] {
		case OPTIGNORE:
			flag |= FLAGIGNORE
		case OPTOMITEMPTY:
			flag |= FLAGOMIEMPTY
		case OPTDIVE:
			flag |= FLAGDIVE
		case OPTWILDCARD:
			flag |= FLAGWILDCARD
		}
	}

	return fieldTag, flag
}

// CopyToStruct struct给struct赋值、map给struct赋值
// receive 被赋值的struct的pointer
// input 持有待赋值的变量，它可以是map,struct,struct的pointer
func CopyToStruct(receive interface{}, input interface{}) (err error) {
	defer func() {
		if catch := recover(); catch != nil {
			err = fmt.Errorf("SetStructField catch: %s", catch)
		}
	}()

	v := reflect.ValueOf(input)
	if v.Kind() == reflect.Map {
		err = setStructFieldByMap(receive, input.(map[string]interface{}))
	} else if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
		err = setStructFieldByStruct(receive, input)
	}
	return err
}

// 使用map给结构体赋值
func setStructFieldByMap(ptr interface{}, m map[string]interface{}) (err error) {
	if len(m) == 0 {
		return nil
	}

	defer func() {
		if catch := recover(); catch != nil {
			err = fmt.Errorf("SetStructFieldByMap catch: %s", catch)
		}
	}()

	var v reflect.Value

	if v, err = getValue(ptr); err != nil {
		return err
	}

	bit := 32 << (^uint(0) >> 63)

	for key, item := range m {
		k1 := v.FieldByName(key).Kind()
		k2 := reflect.ValueOf(item).Kind()

		// CanSet() return false if no field was found or the field is unexported in struct
		if canSet := v.FieldByName(key).CanSet(); !canSet {
			continue
		}

		if err := setStructField(k1, k2, bit, key, item, v); err != nil {
			return err
		}
	}
	return nil
}

func getValue(ptr interface{}) (reflect.Value, error) {
	v := reflect.ValueOf(ptr)
	if v.Kind() != reflect.Ptr {
		return reflect.ValueOf(nil), fmt.Errorf("ptr's type is not ptr")
	}

	v = v.Elem()
	if v.Kind() != reflect.Struct {
		return reflect.ValueOf(nil), fmt.Errorf("ptr is not points to the type of struct")
	}

	return v, nil
}

// 使用struct给结构体赋值
func setStructFieldByStruct(ptr interface{}, input interface{}) (err error) {
	defer func() {
		if catch := recover(); catch != nil {
			err = fmt.Errorf("SetStructFieldByStruct catch: %s", catch)
		}
	}()

	var v reflect.Value

	if v, err = getValue(ptr); err != nil {
		return err
	}

	v2 := reflect.ValueOf(input)
	if v2.Kind() == reflect.Ptr {
		v2 = v2.Elem()
	}
	if v2.Kind() != reflect.Struct {
		return fmt.Errorf("input's type is not Struct or Ptr")
	}

	t := v2.Type()

	bit := 32 << (^uint(0) >> 63)
	var structField reflect.StructField
	numField := t.NumField()
	for i := 0; i < numField; i++ {
		structField = t.Field(i)
		k1 := v.FieldByName(structField.Name).Kind()
		k2 := structField.Type.Kind()

		// CanSet() return false if no field was found or the field is unexported in struct
		if canSet := v.FieldByName(structField.Name).CanSet(); !canSet {
			continue
		}

		if err := setStructField(k1, k2, bit, structField.Name, v2.Field(i).Interface(), v); err != nil {
			return err
		}
	}
	return nil
}

func setStructField(k1, k2 reflect.Kind, bit int, key string, item interface{}, v reflect.Value) error {
	var (
		canSetInt  bool
		canSetUint bool
	)
	if k2.String()[0] == 105 {
		canConvertInt8 := k2 == reflect.Int8
		canConvertInt16 := canConvertInt8 || k2 == reflect.Int16
		canConvertInt32 := canConvertInt16 || k2 == reflect.Int32 || (bit == 32 && k2 == reflect.Int)
		canConvertInt64 := canConvertInt32 || k2 == reflect.Int64 || k2 == reflect.Int
		canConvertInt := (bit == 32 && canConvertInt32) || (bit == 64 && canConvertInt64)
		canSetInt = (k1 == reflect.Int && canConvertInt) || (k1 == reflect.Int8 && canConvertInt8) || (k1 == reflect.Int16 && canConvertInt16) || (k1 == reflect.Int32 && canConvertInt32) || (k1 == reflect.Int64 && canConvertInt64)
	} else if k2.String()[0] == 117 {
		canConvertUint8 := k2 == reflect.Uint8
		canConvertUint16 := canConvertUint8 || k2 == reflect.Uint16
		canConvertUint32 := canConvertUint16 || k2 == reflect.Uint32 || (bit == 32 && k2 == reflect.Uint)
		canConvertUint64 := canConvertUint32 || k2 == reflect.Uint64 || k2 == reflect.Uint
		canConvertUint := (bit == 32 && canConvertUint32) || (bit == 64 && canConvertUint64)
		canSetUint = (k1 == reflect.Uint && canConvertUint) || (k1 == reflect.Uint8 && canConvertUint8) || (k1 == reflect.Uint16 && canConvertUint16) || (k1 == reflect.Uint32 && canConvertUint32) || (k1 == reflect.Uint64 && canConvertUint64)
	}

	switch {
	case k1 == k2:
		v.FieldByName(key).Set(reflect.ValueOf(item))
	case canSetInt:
		v.FieldByName(key).SetInt(reflect.ValueOf(item).Int())
	case canSetUint:
		v.FieldByName(key).SetUint(reflect.ValueOf(item).Uint())
	case (k1 == reflect.Float32 && k2 == reflect.Float32) || (k1 == reflect.Float64 && (k2 == reflect.Float32 || k2 == reflect.Float64)):
		v.FieldByName(key).SetFloat(reflect.ValueOf(item).Float())
	case (k1 == reflect.Complex64 && k2 == reflect.Complex64) || (k1 == reflect.Complex128 && (k2 == reflect.Complex64 || k2 == reflect.Complex128)):
		v.FieldByName(key).SetComplex(reflect.ValueOf(item).Complex())
	default:
		return fmt.Errorf("cannot use %v (type %s) as type %s in assignment", reflect.ValueOf(item).Interface(), k2, k1)
	}
	return nil
}
